package cn.lingyangwl.agile.pay.common.service.impl;

import cn.lingyangwl.agile.pay.common.assembly.PayOrderAssembly;
import cn.lingyangwl.agile.pay.common.constants.Cs;
import cn.lingyangwl.agile.pay.pay.model.rqrs.ChannelRetMsgExt;
import cn.lingyangwl.framework.core.page.PageModel;
import cn.lingyangwl.framework.core.page.TableSupport;
import cn.lingyangwl.framework.data.mate.annotations.FieldBindHandle;
import cn.lingyangwl.framework.mybatis.plus.utils.PageUtil;
import cn.lingyangwl.framework.tool.core.CollectionUtils;
import cn.hutool.core.date.DateTime;
import cn.lingyangwl.agile.pay.model.entity.PayOrder;
import cn.lingyangwl.agile.pay.model.entity.PayWay;
import cn.lingyangwl.agile.pay.model.rqrs.PayOrderQL;
import cn.lingyangwl.agile.pay.model.rqrs.pay.payorder.PayOrderRS;
import cn.lingyangwl.agile.pay.common.manager.PayWayManager;
import cn.lingyangwl.agile.pay.common.mapper.PayOrderMapper;
import cn.lingyangwl.agile.pay.common.service.PayOrderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author shenguangyang
 */
@Service
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements PayOrderService {
    @Resource
    private PayOrderMapper payOrderMapper;

    @Resource
    private PayWayManager payWayManager;

    @Resource
    private PayOrderAssembly payOrderAssembly;

    @Override
    public boolean updateInit2Ing(String payOrderNo, PayOrder payOrder) {
        PayOrder updateRecord = new PayOrder();
        updateRecord.setStatus(PayOrder.STATE_ING);

        //同时更新， 未确定 --》 已确定的其他信息。  如支付接口的确认、 费率的计算。
        updateRecord.setIfCode(payOrder.getIfCode());
        updateRecord.setWayCode(payOrder.getWayCode());
        updateRecord.setMchFeeRate(payOrder.getMchFeeRate());
        updateRecord.setMchFeeAmount(payOrder.getMchFeeAmount());
        updateRecord.setChannelUser(payOrder.getChannelUser());
        updateRecord.setChannelOrderNo(payOrder.getChannelOrderNo());

        return update(updateRecord, PayOrder.lqw().eq(PayOrder::getPayOrderNo, payOrderNo)
                .eq(PayOrder::getStatus, PayOrder.STATE_INIT));
    }

    @Override
    public boolean updateIng2Success(String payOrderNo, ChannelRetMsgExt channelRetMsgExt) {
        PayOrder updateRecord = new PayOrder();
        updateRecord.setStatus(PayOrder.STATE_SUCCESS);
        updateRecord.setChannelOrderNo(channelRetMsgExt.getChannelOrderId());
        updateRecord.setChannelUser(channelRetMsgExt.getChannelUserId());
        updateRecord.setSuccessTime(new Date());

        return update(updateRecord, new LambdaUpdateWrapper<PayOrder>()
                .eq(PayOrder::getPayOrderNo, payOrderNo).eq(PayOrder::getStatus, PayOrder.STATE_ING));
    }

    @Override
    public boolean updateIng2Close(String payOrderNo) {
        PayOrder updateRecord = new PayOrder();
        updateRecord.setStatus(PayOrder.STATE_CLOSED);
        updateRecord.setSuccessTime(new Date());

        return update(updateRecord, new LambdaUpdateWrapper<PayOrder>()
                .eq(PayOrder::getPayOrderNo, payOrderNo).eq(PayOrder::getStatus, PayOrder.STATE_ING));
    }

    @Override
    public void updateInit2Close(String payOrderNo) {
        PayOrder updateRecord = new PayOrder();
        updateRecord.setStatus(PayOrder.STATE_CLOSED);
        updateRecord.setSuccessTime(new Date());

        update(updateRecord, new LambdaUpdateWrapper<PayOrder>()
                .eq(PayOrder::getPayOrderNo, payOrderNo).eq(PayOrder::getStatus, PayOrder.STATE_INIT));
    }

    @Override
    public boolean updateIng2Fail(String payOrderNo, ChannelRetMsgExt channelRetMsgExt) {
        PayOrder updateRecord = new PayOrder();
        updateRecord.setStatus(PayOrder.STATE_FAIL);
        updateRecord.setErrCode(channelRetMsgExt.getChannelErrCode());
        updateRecord.setErrMsg(channelRetMsgExt.getChannelErrMsg());
        updateRecord.setChannelOrderNo(channelRetMsgExt.getChannelOrderId());
        updateRecord.setChannelUser(channelRetMsgExt.getChannelUserId());

        return update(updateRecord, new LambdaUpdateWrapper<PayOrder>()
                .eq(PayOrder::getPayOrderNo, payOrderNo).eq(PayOrder::getStatus, PayOrder.STATE_ING));
    }

    @Override
    public boolean updateIng2SuccessOrFail(String payOrderNo, String updateStatus, ChannelRetMsgExt channelRetMsgExt) {
        if (Objects.equals(updateStatus, PayOrder.STATE_ING)) {
            return true;
        } else if (Objects.equals(updateStatus, PayOrder.STATE_SUCCESS)) {
            return updateIng2Success(payOrderNo, channelRetMsgExt);
        } else if (Objects.equals(updateStatus, PayOrder.STATE_FAIL)) {
            return updateIng2Fail(payOrderNo, channelRetMsgExt);
        }
        return false;
    }


    /**
     * 分页查询 产品订单
     *
     * @return 返回分页数据
     */
    @Override
    @FieldBindHandle
    public IPage<PayOrderRS> pagePayOrder(PayOrderQL req) {
        PageModel page = TableSupport.buildPageRequest();
        LocalDateTime beginTime = Optional.ofNullable(page.getBeginDate()).map(DateTime::toLocalDateTime).orElse(null);
        LocalDateTime endTime = Optional.ofNullable(page.getEndDate()).map(DateTime::toLocalDateTime).orElse(null);

        LambdaQueryWrapper<PayOrder> lqw = Wrappers.<PayOrder>lambdaQuery()
                .eq(StringUtils.isNotEmpty(req.getPayOrderNo()), PayOrder::getPayOrderNo, req.getPayOrderNo())
                .eq(StringUtils.isNotEmpty(req.getStatus()), PayOrder::getStatus, req.getStatus())
                .eq(StringUtils.isNotEmpty(req.getWayCode()), PayOrder::getWayCode, req.getWayCode())
                .eq(StringUtils.isNotEmpty(req.getNotifyStatus()), PayOrder::getNotifyStatus, req.getNotifyStatus())
                .eq(StringUtils.isNotEmpty(req.getAppId()), PayOrder::getAppId, req.getAppId())
                .eq(StringUtils.isNotEmpty(req.getMchNo()), PayOrder::getMchNo, req.getMchNo())
                .ge(Objects.nonNull(page.getBeginDate()), PayOrder::getCreatedAt, beginTime)
                .le(Objects.nonNull(page.getEndDate()), PayOrder::getSuccessTime, endTime)
                .orderByDesc(PayOrder::getCreatedAt);
        lqw.and(StringUtils.isNotEmpty(req.getUnionOrderNo()), e ->
                e.eq(PayOrder::getPayOrderNo, req.getUnionOrderNo())
                .or().eq(PayOrder::getMchOrderNo, req.getUnionOrderNo())
                .or().eq(PayOrder::getChannelOrderNo, req.getUnionOrderNo())
        );
        IPage<PayOrder> pageData = this.page(PageUtil.to(page), lqw);
        List<PayOrderRS> respData = pageData.getRecords().stream()
                .map(payOrderAssembly::toPayOrderRS)
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(respData)) {
            // 获取到所有支付方式
            Map<String, String> payWayMap = payWayManager.list().stream()
                    .collect(Collectors.toMap(PayWay::getWayCode, PayWay::getWayName));
            respData.forEach(e -> e.setWayName(payWayMap.get(e.getWayCode())));
        }
        Page<PayOrderRS> resp = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal());
        resp.setRecords(respData);
        return resp;
    }

    /**
     * 根据主键查询 产品订单
     *
     * @param id 主键
     * @return 返回记录，没有返回null
     */
    @Override
    public PayOrderRS getPayOrderById(Long id) {
        PayOrder entity = this.getById(id);
        return payOrderAssembly.toPayOrderRS(entity);
    }

    @Override
    public PayOrder getByPayOrderNo(String payOrderNo) {
        return this.getOne(PayOrder.lqw().eq(PayOrder::getPayOrderNo, payOrderNo));
    }

    @Override
    public PayOrder getMchOrder(String mchNo, String payOrderNo, String mchOrderNo) {
        LambdaQueryWrapper<PayOrder> lqw = PayOrder.lqw()
                .eq(PayOrder::getMchNo, mchNo)
                .eq(StringUtils.isNotEmpty(payOrderNo), PayOrder::getPayOrderNo, payOrderNo)
                .eq(StringUtils.isNotEmpty(mchOrderNo), PayOrder::getMchOrderNo, mchOrderNo);
        return this.getOne(lqw);
    }

    @Override
    public String getPayOrderNo(String mchNo, String payOrderNo, String mchOrderNo) {
        if (StringUtils.isNotEmpty(payOrderNo)) {
            return payOrderNo;
        }
        LambdaQueryWrapper<PayOrder> lqw = PayOrder.lqw()
                .select(PayOrder::getPayOrderNo)
                .eq(PayOrder::getMchNo, mchNo)
                .eq(StringUtils.isNotEmpty(payOrderNo), PayOrder::getPayOrderNo, payOrderNo)
                .eq(StringUtils.isNotEmpty(mchOrderNo), PayOrder::getMchOrderNo, mchOrderNo);
        return Optional.ofNullable(this.getOne(lqw)).map(PayOrder::getPayOrderNo).orElse("");
    }

    @Override
    public boolean updateNotifySent(String payOrderNo) {
        LambdaUpdateWrapper<PayOrder> luw = PayOrder.luw().eq(PayOrder::getPayOrderNo, payOrderNo)
                .set(PayOrder::getNotifyStatus, Cs.YES);
        return this.update(luw);
    }
}